package com.hainingtong.busapi;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.UUID;

public class BusRequest {

    protected static String sign(String jsonStr) {
        return sign(jsonStr, "hnx");
    }

    protected static String sign(String jsonStr, String token) {
        JSONObject jsonObj = JSONObject.parseObject(jsonStr);
        System.out.println(jsonObj);
        System.out.println("test:" + (jsonObj.get("test") instanceof JSONObject));
        System.out.println("test1:" + (jsonObj.get("test1") instanceof JSONArray));
        jsonObj = generateSignObj(jsonObj, token);
        System.out.println(jsonObj);

        List<String> tmpArr = new ArrayList<>();
        for (Object key : jsonObj.keySet()) {
            tmpArr.add(key.toString());
        }
        System.out.println(tmpArr);
        tmpArr.sort((o1, o2) -> {
            if (null == o1 || null == o2 || "".equals(o1) || "".equals(o2)) {
                return 0;
            }

            char[] c_pre = o1.toCharArray();
            char[] c_next = o2.toCharArray();

            int minSize = Math.min(c_pre.length, c_next.length);

            for (int i = 0; i < minSize; i++) {
                if ((int) c_pre[i] > (int) c_next[i]) {
                    return 1;
                } else if ((int) c_pre[i] < (int) c_next[i]) {
                    return -1;
                }
            }
            if (c_pre.length > c_next.length) {
                return 1;
            }

            return -1;
        });

        System.out.println(tmpArr);


        StringBuilder result = new StringBuilder();

        // 遍历 并且拼接待签名的字符串
        for (int i = 0; i < tmpArr.size(); i++) {
            String key = tmpArr.get(i);
            Object v = jsonObj.getOrDefault(key, "");
            if ((v instanceof JSONObject) || (v instanceof JSONArray)) {
                v = ((JSON) v).toJSONString();
            }
            String value = encodeURIComponent(v.toString());

            value = value.replaceAll("!", "%21");
            value = value.replaceAll("'", "%27");
            value = value.replaceAll("\\(", "%28");
            value = value.replaceAll("\\)!", "%29");
            value = value.replaceAll("\\*", "%2A");

            String rs = key + "=" + value;
            result.append(rs);
            if (i < tmpArr.size() - 1) {
                result.append("&");
            }
        }

        // 直接拼接用于签名的secret
        result.append(generateSignKey());
        // 将待签名的字符串做MD5签名,并转换成大写
        String signStr = md5(result.toString()).toUpperCase();
        jsonObj.put("SIGN", signStr);
        removeKeyFromResult(jsonObj);
        return JSON.toJSONString(jsonObj);
    }

    private static void removeKeyFromResult(JSONObject jsonObj) {
        jsonObj.remove("OAUTH_APPID");
        jsonObj.remove("OAUTH_OPENID");
    }

    /**
     * 通过固定的算法算出参与签名的参数
     *
     * @return String
     */
    private static String generateSignKey() {
        String[] monthCons = new String[]{"January", "February", "March", "April", "May", "June",
                "July", "August", "September", "October", "November", "December"};

        String month = monthCons[Calendar.getInstance().get(Calendar.MONTH)];
        String SALT = "yAqPxwSTwLTwahTZWsdEmZJS";
        String key = md5(SALT + month).toUpperCase();

        key += "FFFFFFFFFFFFFFF";
        return key.substring(0, 16);
    }

    private static String md5(String plainText) {
        byte[] secretBytes;
        try {
            secretBytes = MessageDigest.getInstance("md5").digest(
                    plainText.getBytes());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("没有这个md5算法！");
        }
        StringBuilder md5code = new StringBuilder(new BigInteger(1, secretBytes).toString(16));
        for (int i = 0; i < 32 - md5code.length(); i++) {
            md5code.insert(0, "0");
        }
        return md5code.toString();
    }

    private static JSONObject generateSignObj(JSONObject jsonObj, String token) {
        jsonObj.put("OAUTH_APPID", token);
        jsonObj.put("OAUTH_METHOD", "MD5");
        jsonObj.put("OAUTH_TIMESTAMP", System.currentTimeMillis() / 1000);
        jsonObj.put("OAUTH_NONCE", UUID.randomUUID());

        return jsonObj;
    }

    private static String encodeURIComponent(String s) {
        String result;

        try {
            result = URLEncoder.encode(s, "UTF-8")
                    .replaceAll("\\+", "%20")
                    .replaceAll("\\%21", "!")
                    .replaceAll("\\%27", "'")
                    .replaceAll("\\%28", "(")
                    .replaceAll("\\%29", ")")
                    .replaceAll("\\%7E", "~");
        } catch (UnsupportedEncodingException e) { // This exception should never occur.
            result = s;
        }

        return result;
    }
}
